/**
 * author      : dormi330
 * date        : 2018/6/26
 * project     : mybatis
 * description : 应用层服务
 */

package org.wzq.learn.application;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.wzq.learn.adaptor.*;
import org.wzq.learn.domain.order.*;
import org.wzq.learn.order.InDtoAuditOrder;
import org.wzq.learn.order.InDtoCreateOrder;
import org.wzq.learn.order.InDtoOrderItem;
import org.wzq.learn.order.TradeApi;
import org.wzq.learn.order.query.*;
import org.wzq.learn.utils.CommonUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ApplicationTradeService implements TradeApi, TradeQueryApi {

    @Autowired
    private ItemService itemService;

    @Autowired
    private MemberService memberService;

    @Autowired
    private LogisticalService logisticalService;

    @Autowired
    private CommandPlaceOrder commandPlaceOrder;

    @Autowired
    private CommandAudit commandAudit;

//    @Autowired
//    private CommandPayOrder commandPayOrder;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private OrderQueryRepository orderQueryRepository;

    @Autowired
    private SellerMgmtService sellerMgmtService;

    private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

    /**
     * 新建订单
     *
     * @param inDtoCreateOrder 创建订单的入参
     * @return 订单创建成功与否
     * @throws Exception
     */
    @Override
    public void placeOrder(InDtoCreateOrder inDtoCreateOrder) throws Exception {
        logger.info("{}, {}", CommonUtils.currentClassAndMethod(), inDtoCreateOrder);

        if (inDtoCreateOrder == null) throw new Exception("input arguments cannot be null");

        String tenantCode = inDtoCreateOrder.tenantCode;
        if (null == tenantCode) throw new Exception("tenantCode cannot be null");

        String memberId = inDtoCreateOrder.memberId;
        if (null == memberId) throw new Exception("memberId cannot be null");

        Member member = getMember(tenantCode, memberId);

        MemberAddress address = getMemberAddress(tenantCode, member.getId(), inDtoCreateOrder.addressId);
        List<SkuAndQtyAndFreight> skuAndQtyAndFreightList = getItemSkuAndQtyList(inDtoCreateOrder.orderItemList);

        boolean reduceInventoryOk = inventoryService.reduceInventoryForOrder(skuAndQtyAndFreightList);
        if (!reduceInventoryOk) {
            throw new Exception("inventory not fulfil");
        }

        calculateFreight(skuAndQtyAndFreightList);

        try {
            commandPlaceOrder.placeOrder(tenantCode, member, address, skuAndQtyAndFreightList);
        } catch (Exception ex) {
            // TODO logger
            ex.printStackTrace();
            throw new Exception("fail to place order");
        }
    }

//    /**
//     * 支付 一个已经存在的订单
//     *
//     * @param payOrderDto
//     */
//    @Override
//    public void payOrder(InDtoPayOrder payOrderDto) throws Exception {
//        logger.info("{}, {}", CommonUtils.currentClassAndMethod(), payOrderDto);
//
//        if (null == payOrderDto) {
//            throw new Exception("payOrder, arguments cannot be null");
//        }
//
//        String tenantCode = payOrderDto.tenantCode;
//        String memberId = payOrderDto.memberId;
//        String orderSummaryId = payOrderDto.orderSummaryId;
//        OrderSummary orderSummary = orderQueryRepository.getOrderSummary(tenantCode, memberId, orderSummaryId);
//        if (null == orderSummary) {
//            String errMsg = String.format("payOrder, no such order, tenantCode=%s,  memberId=%s, orderSummaryId=%s", tenantCode, memberId, orderSummaryId);
//            throw new Exception(errMsg);
//        }
//        String payChannel = payOrderDto.payChannel;
//        if (null == payChannel) {
//            throw new Exception("payOrder, payChannel cannot be null");
//        }
//        commandPayOrder.payOrder(orderSummary, payChannel);
//    }

    @Override
    public boolean auditTradeOrder(InDtoAuditOrder inDtoAuditOrder) throws Exception {
        logger.info("{}, {}", CommonUtils.currentClassAndMethod(), inDtoAuditOrder);

        if (null == inDtoAuditOrder) throw new Exception("arguments cannot be null");

        String tenantCode = inDtoAuditOrder.tenantCode;
        if (null == tenantCode) throw new Exception("tenantCode cannot be null");

        String tradeOrderId = inDtoAuditOrder.tradeOrderId;
        if (null == tenantCode) throw new Exception("tradeOrderId cannot be null");

        TradeOrder tradeOrder = getTradeOrder(tenantCode, tradeOrderId);

        boolean canAudit = sellerMgmtService.canUserAuditOrder(inDtoAuditOrder.auditorId, tradeOrder.getSellerId());
        if (!canAudit) throw new Error("cannot auditOne this order");

       return commandAudit.auditOne(tradeOrder, inDtoAuditOrder);
    }

    private TradeOrder getTradeOrder(String tenantCode, String tradeOrderId) throws Exception {
        if (null == tenantCode) throw new Exception("tenantCode cannot be null");
        if (null == tradeOrderId) throw new Exception("tradeOrderId cannot be null");

        TradeOrder tradeOrder = orderQueryRepository.getTradeOrder(tenantCode, tradeOrderId);
        if (null == tradeOrder) {
            String errMsg = String.format("no such tradeOrder, tenantCode=%s,  tradeOrderId=%s", tenantCode, tradeOrderId);
            throw new Exception(errMsg);
        }
        return tradeOrder;
    }

    private Member getMember(String tenantCode, String memberId) throws Exception {
        if (null == tenantCode) throw new Exception("tenantCode cannot be null");
        if (null == memberId) throw new Exception("memberId cannot be null");

        Member member = memberService.memberOfId(tenantCode, memberId);
        if (member == null) {
            String errMsg = String.format("no such member, tenantCode=%s,  memberId=%s", tenantCode, memberId);
            throw new Exception(errMsg);
        }
        return member;
    }

    private MemberAddress getMemberAddress(String tenantCode, String memberId, String addressId) throws Exception {
        if (null == tenantCode) {
            throw new Exception("tenantCode cannot be null");
        }
        if (null == memberId) {
            throw new Exception("memberId cannot be null");
        }
        if (null == addressId) {
            throw new Exception("addressId cannot be null");
        }

        MemberAddress memberAddress = memberService.addressOfMember(tenantCode, memberId, addressId);
        if (memberAddress == null) {
            String errMsg = String.format("no such memberAddress, tenantCode=%s,  memberId=%s, addressId=%s", tenantCode, memberId, addressId);
            throw new Exception(errMsg);
        }
        return memberAddress;
    }

    private List<SkuAndQtyAndFreight> getItemSkuAndQtyList(List<InDtoOrderItem> inDtoOrderItemList) throws Exception {
        if (null == inDtoOrderItemList || inDtoOrderItemList.size() == 0) {
            throw new Exception("no item");
        }

        List<SkuAndQtyAndFreight> itemSkuList = new ArrayList<>(inDtoOrderItemList.size());
        for (InDtoOrderItem inDtoOrderItem : inDtoOrderItemList) {
            ItemSku itemSku = itemService.skuOfId(inDtoOrderItem.sku);
            if (itemSku == null) {
                throw new Exception("item not exist, sku=" + inDtoOrderItem.sku);
            }
            int quantity = inDtoOrderItem.quantity;
            if (quantity <= 0) {
                throw new Exception("item quantity illegal, quantity=" + quantity);
            }
            itemSkuList.add(new SkuAndQtyAndFreight(itemSku, quantity, inDtoOrderItem.remark));
        }
        return itemSkuList;
    }

    private void calculateFreight(List<SkuAndQtyAndFreight> skuAndQtyAndFreightList) throws Exception {
        for (SkuAndQtyAndFreight skuAndQtyAndFreight : skuAndQtyAndFreightList) {
            BigDecimal freight = logisticalService.getFreight(skuAndQtyAndFreight.itemSku, skuAndQtyAndFreight.quanlity);
            if (freight == null) {
                throw new Exception("fail to calculate freight");
            }
            skuAndQtyAndFreight.setFreight(freight);
        }
    }

    @Override
    public List<OutDtoOrderSummary> orderListOfMember(String tenantCode, String memberId) throws Exception {
        logger.info("{}, tenantCode={}, memberId={}", CommonUtils.currentClassAndMethod(), tenantCode, memberId);

        if (null == tenantCode) throw new Exception("tenantCode cannot be null");
        if (null == memberId) throw new Exception("memberId cannot be null");

        List<OrderSummary> summaryList = orderQueryRepository.orderSummaryOfMember(tenantCode, memberId);

        // map eo -> dto
        List<OutDtoOrderSummary> orderListDtoSummary = summaryList.stream().map(oneOrderSummary -> {
            OutDtoOrderSummary outDtoOrderSummary = new OutDtoOrderSummary();
            BeanUtils.copyProperties(oneOrderSummary, outDtoOrderSummary);
            return outDtoOrderSummary;
        }).collect(Collectors.toList());

        return orderListDtoSummary;
    }

    @Override
    public OutDtoOrder orderDetail(String tenantCode, String orderSummaryId) throws Exception {
        logger.info("{}, tenantCode={}, orderSummaryId={}", CommonUtils.currentClassAndMethod(), tenantCode, orderSummaryId);

        if (null == tenantCode) throw new Exception("tenantCode cannot be null");
        if (null == orderSummaryId) throw new Exception("orderSummaryId cannot be null");

        OrderSummary orderSummary = orderQueryRepository.orderSummaryOfId(tenantCode, orderSummaryId);
        if (null == orderSummary) throw new Exception("order not exist, orderSummaryId=" + orderSummaryId);

        OutDtoOrderSummary outDtoOrderSummary = new OutDtoOrderSummary();
        BeanUtils.copyProperties(orderSummary, outDtoOrderSummary);

        List<OutDtoOrderDetail> outDtoOrderDetailList = new ArrayList<>();

        List<TradeOrder> tradeOrderList = orderQueryRepository.tradeOderOfOrderSummary(tenantCode, orderSummaryId);
        if (null == tradeOrderList || tradeOrderList.size() == 0) {
            logger.error("orderSummary存在,但是 没有相关的trade order");
            throw new Exception("tradeOrder not exist for orderSummaryId=" + orderSummaryId);
        }

        for (TradeOrder tradeOder : tradeOrderList) {
            OrderItem orderItem = orderQueryRepository.orderItemOfTradeOrder(tenantCode, tradeOder.getId());
            OrderDelivery orderDelivery = orderQueryRepository.orderDeliveryOfTradeOrder(tenantCode, tradeOder.getId());

            OutDtoTradeOrder outDtoTradeOrder = new OutDtoTradeOrder();
            BeanUtils.copyProperties(tradeOder, outDtoTradeOrder);

            OutDtoOrderItem outDtoOrderItem = new OutDtoOrderItem();
            BeanUtils.copyProperties(orderItem, outDtoOrderItem);

            OutDtoOrderDelivery outDtoOrderDelivery = new OutDtoOrderDelivery();
            BeanUtils.copyProperties(orderDelivery, outDtoOrderDelivery);

            OutDtoOrderDetail outDtoOrderDetail = new OutDtoOrderDetail(outDtoOrderDelivery, outDtoOrderItem, outDtoTradeOrder);
            outDtoOrderDetailList.add(outDtoOrderDetail);
        }

        return new OutDtoOrder(outDtoOrderSummary, outDtoOrderDetailList);
    }
}
